﻿using System;
using System.Text.RegularExpressions;
using System.Xml.Linq;

namespace Bing
{

    /// <summary>
    /// Helper functions for working with the Bing API.
    /// </summary>
    public static class ApiHelper
    {

        /// <summary>
        /// The XML name space Element for Bing
        /// </summary>
        public static string BingNs = "http://schemas.microsoft.com/LiveSearch/2008/04/XML/element";
        /// <summary>
        /// The XML name space for the Ad <see cref="SourceType"/>.
        /// </summary>
        public static string AdsNs = "http://schemas.microsoft.com/LiveSearch/2008/04/XML/ads";
        /// <summary>
        /// The XML name space for the Image <see cref="SourceType"/>.
        /// </summary>
        public static string ImageNs = "http://schemas.microsoft.com/LiveSearch/2008/04/XML/multimedia";
        /// <summary>
        /// The XML name space for the InstantAnswer <see cref="SourceType"/>.
        /// </summary>
        public static string InstantAnswerNs = "http://schemas.microsoft.com/LiveSearch/2008/04/XML/instantanswer";
        /// <summary>
        /// The XML name space for the Encarta <see cref="SourceType"/>.
        /// </summary>
        public static string EncartaNs = "http://schemas.microsoft.com/LiveSearch/2008/04/XML/encarta";
        /// <summary>
        /// The XML name space for the <see cref="FlightStatus"/> <see cref="SourceType"/>.
        /// </summary>
        public static string FlightStatusNs = "http://schemas.microsoft.com/LiveSearch/2008/04/XML/element/flightstatus";
        /// <summary>
        /// The XML name space for the MobileWeb <see cref="SourceType"/>.
        /// </summary>
        public static string MobileWebNs = "http://schemas.microsoft.com/LiveSearch/2008/04/XML/mobileweb";
        /// <summary>
        /// The XML name space for the News <see cref="SourceType"/>.
        /// </summary>
        public static string NewsNs = "http://schemas.microsoft.com/LiveSearch/2008/04/XML/news";
        /// <summary>
        /// The XML name space for the Phonebook <see cref="SourceType"/>.
        /// </summary>
        public static string PhonebookNs = "http://schemas.microsoft.com/LiveSearch/2008/04/XML/phonebook";
        /// <summary>
        /// The XML name space for the RelatedSearch <see cref="SourceType"/>.
        /// </summary>
        public static string RelatedSearch = "http://schemas.microsoft.com/LiveSearch/2008/04/XML/relatedsearch";
        /// <summary>
        /// The XML name space for the Spell <see cref="SourceType"/>.
        /// </summary>
        public static string SpellNs = "http://schemas.microsoft.com/LiveSearch/2008/04/XML/spell";
        /// <summary>
        /// The XML name space for the Translation <see cref="SourceType"/>.
        /// </summary>
        public static string TranslationNs = "http://schemas.microsoft.com/LiveSearch/2008/04/XML/translation";
        /// <summary>
        /// The XML name space for the Video <see cref="SourceType"/>.
        /// </summary>
        public static string VideoNs = "http://schemas.microsoft.com/LiveSearch/2008/04/XML/multimedia";
        /// <summary>
        /// The XML name space for the Web <see cref="SourceType"/>.
        /// </summary>
        public static string WebNs = "http://schemas.microsoft.com/LiveSearch/2008/04/XML/web";

        /// <summary>
        /// Gets the element value or default.
        /// </summary>
        /// <typeparam name="T">The Data Type that you want returned.</typeparam>
        /// <param name="node">The node.</param>
        /// <param name="elementName">Name of the element.</param>
        /// <param name="nameSpace">The name space.</param>
        /// <returns>This function will return the element value for the specified node and name space or the Data Types default value.</returns>
        public static T GetElementValueOrDefault<T>(XNode node, string elementName, string nameSpace)
        {
            if (node == null) return default(T);

            XElement element = ((XElement) node).Element(XName.Get(elementName, nameSpace));
            if (element == null) return default(T);
            return (T) Convert.ChangeType(element.Value, typeof (T));

        }

        /// <summary>
        /// Gets the element value or default.
        /// </summary>
        /// <typeparam name="T">The Data Type that you want returned.</typeparam>
        /// <param name="node">The node.</param>
        /// <param name="elementName">Name of the element.</param>
        /// <returns>This function will return the element value for the specified node and name space or the Data Types default value.</returns>
        public static T GetElementValueOrDefault<T>(XNode node, string elementName)
        {
            if (node == null) return default(T);

            XElement element = ((XElement)node).Element(elementName);
            if (element == null) return default(T);
            return (T)Convert.ChangeType(element.Value, typeof(T));

        }

        /// <summary>
        /// Gets the URL.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static string GetUrl(string value)
        {
            Regex regex = new Regex(
                "(?<Protocol>\\w+):\\/\\/(?<Domain>[\\w@][\\w.:@]+)\\/?[\\w\\.?=%&=\\-@/$,]*\r\n",
                RegexOptions.IgnoreCase
                | RegexOptions.CultureInvariant
                | RegexOptions.IgnorePatternWhitespace
                | RegexOptions.Compiled
                );

            Match m = regex.Match(value);
            return m.Value;

        }

        #region Enums

        // TODO: Documentation Error: http://msdn.microsoft.com/en-us/library/dd250882.aspx, Missing VideoOptions Enumeration
        /// <summary>
        /// Contains members that specify the level of AdultOption filtering used in this query.
        /// </summary>
        public enum AdultOption
        {
            /// <summary>
            /// Specifies that filtering is not used in a query.
            /// </summary>
            Off,
            /// <summary>
            /// Specifies that results of a query should not include sexually explicit images or videos, but may include sexually explicit text.
            /// </summary>
            Moderate,
            /// <summary>
            /// Specifies that results of a query should not include sexually explicit text, images, or videos. 
            /// </summary>
            Strict
        }

        /// <summary>
        /// Contains members that can affect the sort order of News results.
        /// </summary>
        public enum NewsSortOption
        {
            /// <summary>
            /// Specifies that News results be returned in chronological order.
            /// </summary>
            Date,
            /// <summary>
            /// Specifies that News results be returned by relevance of the response to the query.
            /// </summary>
            Relevance
        }

        /// <summary>
        /// Contains members that specify the sort order of PhoneBook results.
        /// </summary>
        public enum PhonebookSortOption
        {
            /// <summary>
            /// Specifies that PhoneBook results be returned in the default order, by distance from the Location specified in the SearchRequest, or by distance from the center of the Location specified in the query.
            /// </summary>
            Default,
            /// <summary>
            /// Specifies that PhoneBook results be returned by distance from the Location specified in the SearchRequest, or by distance from the center of the Location specified in the query.
            /// </summary>
            Distance,
            /// <summary>
            /// Specifies that PhoneBook results be returned by relevance of the response to the query.
            /// </summary>
            Relevance
        }

        /// <summary>
        /// Contains members that specify options related to a request.
        /// </summary>
        public enum SearchOption
        {
            /// <summary>
            /// Specifying this option prevents Bing from inferring location from the terms of a query. 
            /// </summary>
            DisableLocationDetection,
            /// <summary>
            /// Specifying this option means that highlighting of (typically, query) terms will be enabled within results.
            /// </summary>
            EnableHighlighting
        }

        /// <summary>
        /// Each member of this enumeration corresponds to a SourceType. A SourceType is a source of information accessible to the API.
        /// </summary>
        public enum SourceType
        {
            /// <summary>
            /// Use the Ad SourceType to incorporate advertisements into your application.
            /// </summary>
            Ad,
            /// <summary>
            /// Full-size image and thumbnail image information, including the file size in bytes (if available), height and width in pixels (if available), and the URI to the full-size image or thumbnail.
            /// </summary>
            Image,
            /// <summary>
            /// Answers. The result fields returned for requests that specify InstantAnswer vary based on the value or values specified for the Query property. InstantAnswer results can include Encarta, FlightStatus, Finance, Music, Sports, Weather, and Movie ShowTimes. For the Version 2.0 release, results include Encarta and FlightStatus only. Other results are available by invitation.
            /// </summary>
            InstantAnswer,
            /// <summary>
            /// Mobile Web page results (primarily Extensible Hypertext Markup Language (XHTML) and Wireless Markup Language (WML).
            /// </summary>
            MobileWeb,
            /// <summary>
            /// Results from online news services.
            /// </summary>
            News,
            /// <summary>
            /// Results from online White Pages (residential) and Yellow Pages (commercial) entries.
            /// </summary>
            Phonebook,
            /// <summary>
            /// Suggestions for other searches related to the query term or terms.
            /// </summary>
            RelatedSearch,
            /// <summary>
            /// Spelling suggestions.
            /// </summary>
            Spell,
            /// <summary>
            /// Translated results for a queried.
            /// </summary>
            Translation,
            /// <summary>
            /// Video results.
            /// </summary>
            Video,
            /// <summary>
            /// Web page results.
            /// </summary>
            Web
        }

        /// <summary>
        /// Contains members that specify options for a request to the Web <see cref="SourceType"/>.
        /// </summary>
        public enum WebSearchOption
        {
            /// <summary>
            /// Specifying this option prevents Bing from suppressing more than two results from the same top-level URL for a request. 
            /// </summary>
            DisableHostCollapsing,
            // TODO: Documentation error: http://msdn.microsoft.com/en-us/library/dd250969.aspx, extra period in description.
            /// <summary>
            /// Specifying this option prevents Bing from altering the query string. Such alteration may have been done in order to correct apparent spelling error in the original query string.
            /// </summary>
            DisableQueryAlterations
        }

        /// <summary>
        /// Contains members that can affect the sort order of Video results.
        /// </summary>
        public enum VideoSortOption
        {
            /// <summary>
            /// Specifies that Video results be returned in chronological order.
            /// </summary>
            Date,
            /// <summary>
            /// Specifies that Video results be returned by relevance of the response to the query.
            /// </summary>
            Relevance
        }

        /// <summary>
        /// Identifies this result as "breaking news" or not "breaking news."
        /// </summary>
        public enum BreakingNewsType
        {
            /// <summary>
            /// Indicates Not Breaking News
            /// </summary>
            NotBreakingNews = 0,
            /// <summary>
            /// Indicates Breaking News
            /// </summary>
            BreakingNews = 1
        }

        #endregion Enums
    }
}
